/*
 PureMVC AS3 Demo - Flex Application Skeleton 
 Copyright (c) 2007 Daniele Ugoletti <daniele.ugoletti@puremvc.org>
 Your reuse is governed by the Creative Commons Attribution 3.0 License
*/
package com.larson.creator.model
{
	import com.larson.creator.*;
	import com.larson.creator.model.vo.*;
	import com.larson.creator.view.*;
	import com.motiondraw.LineGeneralization;
	
	import mx.collections.ArrayCollection;
	import mx.formatters.NumberFormatter;
	import mx.rpc.IResponder;
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;
	import mx.rpc.soap.mxml.Operation;
	import mx.rpc.soap.mxml.WebService;
	
	import org.puremvc.as3.interfaces.*;
	import org.puremvc.as3.patterns.proxy.Proxy;
	
	
    /**
     * A proxy for read the config file
     */
    public class FileConvertProxy extends Proxy implements IProxy, IResponder
    {
		public static const NAME:String = "FileConvertProxy";
		
		//public var rawData:ArrayCollection;
		public var correctedData:ArrayCollection;
		public var lineData:ArrayCollection;
		public var minElevation:Number = 0;
		public var maxElevation:Number = 0;
		private var slopeFormatter:NumberFormatter;		
		private var distanceFormatter:NumberFormatter;		
		private var gpsFormatter:NumberFormatter;	
		
		private var elevationIndex:Number;	
		
		private var service:WebService;
		private var elevationOp:Operation;

		private var elevationList:Array;
		private var controlsVO:ControlsVO;
		private var correctionStatus:Boolean;
		
		public function FileConvertProxy ( data:Object = null ) {
            super ( NAME, data );
            slopeFormatter = new NumberFormatter();
            distanceFormatter = new NumberFormatter();
            gpsFormatter = new NumberFormatter();
            slopeFormatter.precision = 2;
            slopeFormatter.useNegativeSign = true;
            slopeFormatter.useThousandsSeparator = true;
            distanceFormatter.precision = 3;
            distanceFormatter.useNegativeSign = true;
            distanceFormatter.useThousandsSeparator = true;
            gpsFormatter.precision = 8;
            gpsFormatter.useNegativeSign = true;
            gpsFormatter.useThousandsSeparator = true;
            service = new WebService();
            service.wsdl = "http://gisdata.usgs.gov/xmlwebservices2/elevation_service.asmx?WSDL";
            service.loadWSDL();
            service.service = "Elevation_Service";
            service.port = "Elevation_ServiceSoap";
            elevationOp = service.getOperation("getElevation") as Operation;
            elevationOp.resultFormat = "xml";
            elevationOp.addEventListener(ResultEvent.RESULT, this.result);
            elevationOp.addEventListener(FaultEvent.FAULT, this.fault);
            elevationOp.showBusyCursor = true;

        }
		
		public function correctElevation(trackList:Array, vo:ControlsVO):void {
			elevationIndex = 0;
			controlsVO = vo;
			elevationList = trackList;
			elevationServiceCall();
		}
		
		private function elevationServiceCall(): void {
			var lat:String = elevationList[elevationIndex].latitude.toString();
			var long:String = elevationList[elevationIndex].longitude.toString();
			elevationOp.send(long, lat, "meters", "-1", "1");

		}

		// this is called when the delegate receives a result from the service
		public function result( e:Object ) : void
		{
			if( elevationIndex < elevationList.length - 1) {
				// put the new data into our objects
				default xml namespace = "http://gisdata.usgs.gov/XMLWebServices2/";
				var response:XML = new XML(e.result.toString());
				var newElevation:String = response.*[0].*[0].*[0].*[2].*[0].toString();
				if( newElevation.length > 0) {
					elevationList[elevationIndex].elevation = newElevation;
					trace("Elevation: " + newElevation);
				}
				elevationIndex += 1;
				elevationServiceCall();
			}
			else {
				trace("Elevation Service call Done");
				correctionStatus = true;
				//processData( elevationList, controlsVO );
			}
		}
		
		// this is called when the delegate receives a fault from the service
		public function fault( rpcEvent : Object ) : void 
		{
			trace("Elevation Service call failed");
		}

		
		private function elevationCheck( el:Number ):void {
			if( el < minElevation ) {
				minElevation = el;
			}
			if( el > maxElevation ) {
				maxElevation = el;
			}
		}
		
		public function processData( trackList:Array, pLineData:ArrayCollection, cVO:ControlsVO ):void {
			
			var idx:Number = 0;
		
			var len:Number = trackList.length;
			var segText:String;
			var elevationChg:Number;
			var totalDistance:Number = 0;
			var lineArray:Array = new Array();
			
			//rawData = new ArrayCollection();
			lineData = new ArrayCollection();
			minElevation = 1000000;
			maxElevation = 0;
			
			if( cVO.elInterpolation ) {
				interpolateElevation(trackList);
			}

			for(idx = 0; idx < len - 1; idx++) {
				var rawPoint1:CourseDataVO = trackList[idx];
				var rawPoint2:CourseDataVO = trackList[idx + 1];
				
				elevationCheck(rawPoint1.elevation);
				elevationCheck(rawPoint2.elevation);
								
				//rawData.addItem(rawPoint2);
				var lData:LineDataVO = new LineDataVO(); 
				totalDistance += new Number(distanceFormatter.format(rawPoint2.length));
				lData.x = totalDistance;
				lData.y = rawPoint2.elevation;
				lData.slope = rawPoint2.slope;
				lineArray.push(lData);
			}
			// Smooth the line data if indicated.
			if( cVO.elSmoothing == true ) {
				var lineGeneralization:LineGeneralization = new LineGeneralization();
				for( idx = 0; idx < new Number(cVO.smFactor); idx++ ) {
					lineArray = lineGeneralization.smoothMcMaster(lineArray, 5);
				}
			}
			for( idx = 0; idx < lineArray.length; idx++ ) {
				lineArray[idx].y = lineArray[idx].y - minElevation;
			}
			
			correctedData = new ArrayCollection();
			for( idx = 0; idx < (pLineData.length -1); idx++ ) {
				var segDistance:Number = new Number(distanceFormatter.format(pLineData[idx+1].x - pLineData[idx].x));
				var segSlope:Number = new Number(slopeFormatter.format(getElevationChange(lineArray[idx+1].y - lineArray[idx].y, segDistance)));
				lineArray[idx+1].slope = segSlope;
				var newSeg:LineDataVO = new LineDataVO();
				
				newSeg.x = segDistance;
				newSeg.y = segSlope;
				correctedData.addItem(newSeg);

			}
			lineData = new ArrayCollection(lineArray);

			this.sendNotification( ApplicationFacade.CONVERT_FILE_COMPLETE );

		}
		
		private function getElevationChange(elChange:Number, dist:Number):Number {

			var meterDistance:Number = dist * 1000;
			var slope:Number;
			if( meterDistance > 0 ) {
				slope = (elChange/meterDistance) * 100;
			} else {
				slope = 0;
			}
			return slope;
		}
		
		private function interpolateElevation( trackList:Array ):void {
			var idx:Number = 0;
			var lastElevation:Number;
			var intStartIndex:Number = 0;
			var intEndIndex:Number;
			var nextElevation:Number;
			var interpolationCount:Number = 0;
			var elevationChg:Number;
			var len:Number = trackList.length;
						
				for(idx = 0; idx < len - 1; idx++) {
					var pt:CourseDataVO = trackList[idx];
					if( pt.elevation == 0 ) {
						interpolationCount += 1;
					} else {
						if( interpolationCount > 0) {
							intStartIndex = idx - interpolationCount;
							if( intStartIndex == 0 ) {
								for( var x:Number = 0; x < idx; x++ ) {
									trackList[x].elevation = pt.elevation;
								}
							} else {
								// Take current elevation and then back-fill elevation
								nextElevation = pt.elevation;
								lastElevation = trackList[intStartIndex-1].elevation;
								elevationChg = (nextElevation - lastElevation)/interpolationCount;
								for( var y:Number = intStartIndex; y < idx; y++ ) {
									trackList[y].elevation = lastElevation + (((y-intStartIndex)+1)*elevationChg);
								}
							}
						}
						interpolationCount = 0;
					}
				}
			
		}
		
		
	}
}